package tpi.obj3.planificador.extensions

import tpi.obj3.planificador.planificador.Clase
import java.util.List
import tpi.obj3.planificador.validation.Intervalo
import tpi.obj3.planificador.planificador.Materia
import tpi.obj3.planificador.planificador.Planificacion
import tpi.obj3.planificador.planificador.Aula
import tpi.obj3.planificador.planificador.Elemento
import tpi.obj3.planificador.planificador.Modelo
import tpi.obj3.planificador.planificador.Docente
import java.util.ArrayList
import tpi.obj3.planificador.planificador.Asignacion
import tpi.obj3.planificador.planificador.Exclusiva
import tpi.obj3.planificador.planificador.Semi
import tpi.obj3.planificador.planificador.Simple
import org.eclipse.emf.common.util.EList

class ExtensionMethods {

	////////////////////////////////////////
	///////////// MATERIA EXTENSION METHODS
	////////////////////////////////////////
	def aulasAsignadas(Materia materia) {
		materia.clasesDeMateria.map[c | c.aula]
	}
	
	def List<Clase> clasesDeMateria(Materia materia) {
		materia.modelo.planificacion.todasLasClases.filter[c | c.materia == materia].toList
	}
	
	def horasAsignadas(Materia materia) {
		materia.clasesDeMateria.map[c | c.fin - c.inicio].reduce[x, y| x + y]
	}
	
	def tieneDocenteAsignado(Materia materia) {
		(!materia.modelo.asignaciones.filter[a | a.materia == materia].empty) &&
		materia.modelo.planificacion.materias.contains(materia)
	}
	////////////////////////////////////////
	////////////////////////////////////////
	
	//////////////////////////////////////
	///////////// CLASE EXTENSION METHODS
	//////////////////////////////////////
	def boolean puedeAsignarAula(Clase clase) {
		clase.aula.cumpleRequerimientos(clase.materia)
	}
	
	def boolean tieneElTotalDeDiasAsignados(Clase clase) { 
		clase.materia.clasesDeMateria.size == clase.materia.dias
	}
	
	def boolean tieneElTotalDeHorasAsignadas(Clase clase) {
		clase.materia.horasAsignadas == clase.materia.horas
	}
	
	def horarios(List<Clase> clases) {
		clases.map[c | c.horario]
	}
	
	def boolean seSuperponeCon(Clase clase1, Clase clase2) {
		clase1.horario.seSuperponeCon(clase2.horario) && clase1.aula == clase2.aula
	}
	
	def horario(Clase clase) {
		new Intervalo(clase.inicio, clase.fin)
	}
	
	def turnoMañana() {
		new Intervalo(8, 13)	
	}

	def turnoTarde() {
		new Intervalo(13, 18)	
	}

	def turnoNoche() {
		new Intervalo(18, 22)	
	}
	
	def esTurnoMañana(Clase it) {
		horario.seSuperponeCon(turnoMañana)
	}

	def esTurnoTarde(Clase it) {
		horario.seSuperponeCon(turnoTarde)
	}

	def esTurnoNoche(Clase it) {
		horario.seSuperponeCon(turnoNoche) 
	}
	//////////////////////////////////////
	//////////////////////////////////////
	
	/////////////////////////////////////
	///////////// AULA EXTENSION METHODS
	/////////////////////////////////////
	def cumpleRequerimientos(Aula aula, Materia materia) {
		materia.requerimientos.forall[r | aula.recursos.contains(r)]
	}
	/////////////////////////////////////
	/////////////////////////////////////
	
	/////////////////////////////////////////////
	//////////// PLANIFICACION EXTENSION METHODS
	/////////////////////////////////////////////
	def todosLosDias(Planificacion it) {
		#[clasesLunes, clasesMartes, clasesMiercoles, clasesJueves, clasesViernes, clasesSabado]
	}

	def todasLasClases(Planificacion planificacion) {
		planificacion.todosLosDias.flatten 
	}
	
	def aulaMasUtilizada(Planificacion it) {
		todasLasAulas.reduce[a1, a2| 
			if(usosDeUnAula(a1) < usosDeUnAula(a2)) a2  else a1
		]
	}
	
	def usosDeUnAula(Planificacion planificacion, Aula aula) {
		planificacion.todasLasAulas.filter[a | a == aula].size
	}

	def todasLasAulas(Planificacion planificacion) {
		planificacion.todasLasClases.map[c | c.aula]
	}
	
	def horarioAbierto() {
		new Intervalo(8, 22)	
	}
	
	def cantMateriasMañana(Planificacion planificacion) {
		planificacion.cantMateriasQueCumplenTurno[esTurnoMañana]
	}
	
	def cantMateriasTarde(Planificacion planificacion) {
		planificacion.cantMateriasQueCumplenTurno[esTurnoTarde]
	}
	
	def cantMateriasNoche(Planificacion planificacion) {
		planificacion.cantMateriasQueCumplenTurno[esTurnoNoche]
	}
	
	def cantMateriasQueCumplenTurno(Planificacion planificacion, (Clase)=>boolean condition) {
		planificacion.todasLasClases
						.filter[c | condition.apply(c)]
						.map[c | c.materia]
						.eliminarRepetidos
						.size
	}
	/////////////////////////////////////////////
	/////////////////////////////////////////////

	////////////////////////////////////////
	//////////// ELEMENTO EXTENSION METHODS
	////////////////////////////////////////
	def modelo(Elemento elemento) {
		elemento.eContainer as Modelo
	}
	////////////////////////////////////////
	////////////////////////////////////////
	
	//////////////////////////////////////
	//////////// MODELO EXTENSION METHODS
	//////////////////////////////////////
		def asignaciones(Modelo modelo) {
		modelo.elementos.filter(Asignacion)
	}
	
	def docentes(Modelo modelo) {
		modelo.asignaciones
				.map[a | a.docente]
				.eliminarRepetidos
	}
	
	def <E> eliminarRepetidos(Iterable<E> list) {
		val sinReps = new ArrayList<E>
		for(e : list) {
			if(!sinReps.contains(e)) 
				sinReps += e 
		}
		sinReps
	}
	
	def materiasQueDicta(Modelo modelo, Docente docente) {
		modelo.asignaciones
				.filter[a | a.docente.name == docente.name]
				.map[d | d.materia]
				.eliminarRepetidos
	}
	//////////////////////////////////////
	//////////////////////////////////////
	
	///////////////////////////////////////
	//////////// DOCENTE EXTENSION METHODS
	///////////////////////////////////////
	def puedeAsignarse(Docente docente) {
		docente.dedicacion.noLlegoAlLimite(docente)
	}
	
	def cantidadAsignaciones(Docente docente) {
		docente.modelo.asignaciones.filter[a | a.docente.name == docente.name].size
	}
	
	def dispatch boolean noLlegoAlLimite(Simple simple, Docente docente) {
		docente.cantidadAsignaciones <= 1 
	}
	
	def dispatch boolean noLlegoAlLimite(Semi semi, Docente docente) {
		docente.cantidadAsignaciones == 2
	}
	
	def dispatch boolean noLlegoAlLimite(Exclusiva exclusiva, Docente docente) {
		docente.cantidadAsignaciones >= 2 && docente.cantidadAsignaciones <= 5
	}
	
	def dispatch msjError(Simple simple, Docente docente)'''
	El docente «docente.name» solo puede ser asignado/a a una unica materia
	'''
	
	def dispatch msjError(Semi semi, Docente docente)'''
	El docente «docente.name» debe ser asignado/a a dos materias, sin excepcion
	'''
	
	def dispatch msjError(Exclusiva exclusiva, Docente docente)'''
	El docente «docente.name» solo puede asignarse desde dos a cinco materias
	'''
	///////////////////////////////////////
	///////////////////////////////////////
	
	
}